﻿/**
 * 用于GO的状态管理。管理所有组件的状态，所有组件订阅事件，同步状态
 */
"use strict"
var Events = require('events');

class GoStateManager {
    constructor() {
        this.initState();
        this.initList();
        this.eventEmitter = new Events.EventEmitter();
        this.eventEmitter.setMaxListeners(500);

        var t1 = this.getDefaultPieceState();
        var t2 = this.getDefaultPieceState();

    };

    //一个棋子的初始状态
    getDefaultPieceState(){
        return {visibility:'hidden',num:0,black:false};
    }

    //所有棋子的初始状态
    getDefaultPieces(){
        var pieces=[];//所有棋子状态
        for(var i=0 ; i<19*19; i++) pieces.push(this.getDefaultPieceState());
        return pieces;
    }

    initState(){
        //指示当前要下的子的状态，该状态使用后，调用next方法，切换状态
        this.current= {
            index:1,//当前步数
            goBlack:true,//是否是黑子，指行棋时
            placeBlack:true,//是否是黑子，指布局时
            numShow:false,//是否显示数字
            place:false,//是否是布局摆子，如果是，不改变当前步数，布局时摆的棋子上面不显示数字
        };
        //所有棋子的状态
        this.pieces = this.getDefaultPieces();

    }

    //初始化重做、撤销两个队列
    initList(){
        this.undoList = [];//后进先出队列
        this.redoList = [];//后进先出队列
    }

    //将状态压栈，保存，保存的是对象的副本。
    pushUndoList(current,pieces){
        this.undoList.push({
            current:this.cloneObject(current),
            pieces:this.cloneObject(pieces)
        });

    }

    //清空RedoList，当执行下一步时执行此方法
    clearRedoList(){
        var len=this.redoList.length;
        if(len>0)
            this.redoList.splice(0,len);
    }

    //将状态压栈，保存，保存的是对象的副本。
    pushRedoList(current,pieces){
        this.redoList.push({
            current:this.cloneObject(current),
            pieces:this.cloneObject(pieces)
        });
    }

    //弹出队列中的元素，复制一个副本
    popList(list) {
        var record = list.pop();
        return {
            current: this.cloneObject(record.current),
            pieces: this.cloneObject(record.pieces)
        }
    }

    //输出链表内容，用于调试
    printList(list){
        for(var i=0; i<list.length; i++){
            console.log("第%d步：%s",i,this.getVisiblePieces(list[i].pieces));
        }

    }

    getVisiblePieces(pieces){
        var info = "";
        for(var j=0; j<19*19; j++){
            if(pieces[j].visibility=='visible'){
                info = info+pieces[j].num+',';
            }
        }
        //console.log("可见棋子序号："+info);
        return info;
    }

    //撤销
    undo(){
        if (this.undoList.length==0){
            console.log("不能撤销了！");
            return;
        }

        //当前状态压入RedoList
        this.pushRedoList(this.current,this.pieces);

        var record = this.popList(this.undoList)
        this.current = record.current;
        this.pieces = record.pieces;

        this.pubCurrentChange();
        this.pubPieceChange();
    }

    //前进一步
    redo(){
        if (this.redoList.length==0){
            console.log("不能前进了！");
            return;
        }

        this.pushUndoList(this.current,this.pieces);

        var record = this.popList(this.redoList)
        this.current = record.current;
        this.pieces = record.pieces;

        //this.printList(this.undoList);
        //this.printList(this.redoList);

        this.pubCurrentChange();
        this.pubPieceChange();

    }

    //订阅当前状态变化事件
    subCurrentChange(listener) {
        //console.log("订阅状态事件!");
        this.eventEmitter.addListener('currentChange',listener);
    }

    //状态当前变化事件发生，通知监听器
    pubCurrentChange(){
        //console.log("发布状态事件");
        this.eventEmitter.emit("currentChange",this.current);
    }

    //订阅棋子状态变化事件
    subPieceChange(listener) {
        //console.log("订阅棋子事件!");
        this.eventEmitter.addListener('pieceChange',listener);
    }

    //状态棋子变化事件发生，通知监听器
    pubPieceChange(){
        //console.log("发布棋子事件");
        //传递数据，对解耦有一点儿帮助
        this.eventEmitter.emit("pieceChange",this.pieces);
    }

    //推进当前状态到下一步
    //这个函数内部调用
    next(){
        this.printList(this.undoList);

        if(this.current.place==true){
            //如果是布局状态，不改变编号、颜色
        }
        else{
            this.current.index++;
            this.current.goBlack=!this.current.goBlack;
        }
        this.pubCurrentChange();

    }

    //克隆对象
    //数组被jquery复制后，变成了类数组(伪数组)，不带有length方法，这个也比较坑
    cloneObject(object){
        return $.extend(true,{},object);//深层次复制。这个比较坑
    }

    //是否处于布局状态
    isPlace(){
        return this.current.place==true;
    }

    //设置布局状态
    setPlace(bBlack){
        this.current.place=true;
        this.current.placeBlack=(bBlack==true);
        this.pubCurrentChange();
    }

    //重新开始
    restart(){
        this.initState();
        this.initList();
        this.pubCurrentChange();
        this.pubPieceChange();
    }

    //返回当前的步数
    getCurrentIndex(){
        return this.current.index;
    }

    //返回当前状态
    getCurrent(){
        return this.current;
    }

    //返回所有棋子状态
    getPieces(){
        return this.pieces;
    }

    //设置先行方
    setFirst(bBlack) {
        this.current.place = false;
        //仅处于第一步时，可以改变行棋的黑白颜色
        if( this.current.index==1){
            this.current.goBlack=(bBlack==true);
        }
        this.pubCurrentChange();

    }

    //在棋上点击
    //如果棋子状态变化，则返回为true，否则返回false
    clickPiece(index){
        //每次下一步之前的状态都记下来，以便能够回退
        this.pushUndoList(this.current,this.pieces);
        this.clearRedoList();

        var state=this.pieces[index];//应该传递的是指针，相当于起了个别名，实际对应同一块内存地址

        if (state.visibility=='visible' && this.isPlace()==false){//棋子可见、非布局状态
            console.log("棋子可见、非布局状态，退出！");
            return false;
        }
        if (state.visibility=='visible' && this.isPlace()==true && state.num!=0){//棋子可见、布局状态，且非布局棋子
            console.log("棋子可见、布局状态，且非布局棋子，退出！");
            return false;
        }

        //console.log('可以修改棋子状态');
        if (this.isPlace()==false){//行棋中
            state.num = this.current.index;
            state.black = this.current.goBlack;
            state.visibility = 'visible';
        }
        else{//布局
            state.num=0;//布局状态下，放的棋子，其数字设置为0
            //如果原来棋子已经显示，且颜色相同，用是布局摆的棋子，设置其隐藏
            if(state.visibility=='visible' && this.current.placeBlack==state.black && state.num==0){
                state.visibility = 'hidden';
                //棋子颜色不重要，下次再显示时，会设置颜色
            }
            else{
                state.black = this.current.placeBlack;
                state.visibility = 'visible';
            }
        }

        this.pubPieceChange();
        //StateManager.setPieceState(this.state.pieceId,state);//这里有点儿乱
        //这个放最后，完成大大压栈工作
        this.next();
        //this.setState(state);
        return true;
    }

}

module.exports = new GoStateManager();